package utilities.JDBC;

import utilities.input.arrangement.MySqlInit;

import java.sql.*;
import java.util.ArrayList;
import java.util.List;

/**
 * 对数据库中所有的数据条目的查询逻辑进行高度抽象化的类
 * @param <T> 包装数据对象的类
 */
public class Database<T> {
    private PreparedStatement sql;
    private final Connection conn;

    /**
     * 关于构造sql语句的接口
     */
    @FunctionalInterface
    public interface SqlCode{
        /**
         * 函数接口 向外部暴露sql语句逻辑
         * @param connection 数据库连接
         * @return 返回一个已经预编译完成并可以运行的sql语句
         * @throws SQLException 可能发生的错误
         */
        PreparedStatement getSql(Connection connection) throws SQLException;
    }

    /**
     * 关于构造数据对象的接口
     */
    @FunctionalInterface
    public interface ToObject<T>{
        /**
         * 函数接口 向外部暴露对象构造逻辑
         * @param set 得到的结果集
         * @return 返回一个构造完成的对象
         * @throws SQLException 发生的结果集错误
         */
        T getObject(ResultSet set) throws SQLException;
    }

    public Database() {
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");

            this.conn = DriverManager.getConnection(
                    "jdbc:mysql://127.0.0.1:3306/"+ MySqlInit.databaseName,
                    MySqlInit.name ,
                    MySqlInit.password
            );
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } catch (ClassNotFoundException e){
            throw new RuntimeException(e);
        }
    }

    public Database(String databaseName) {
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");

            this.conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/"+databaseName,"root","88888888");
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } catch (ClassNotFoundException e){
            throw new RuntimeException(e);
        }
    }

    public Database(String databaseName,String useName, String password) {
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");

            this.conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/"+databaseName,useName,password);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } catch (ClassNotFoundException e){
            throw new RuntimeException(e);
        }
    }

    /**
     * 运行自定义的sql更新语句
     * @param code 自定义的sql
     * @return 运行sql的结果
     */
    public int executeUpdate(SqlCode code){
        try {
            sql = code.getSql(conn);
            return sql.executeUpdate();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 运行自定义的sql查询语句
     * @param code 自定义的sql
     * @return 完成指定查询的结果集
     */
    public ResultSet executeSelect(SqlCode code){
        try {
            sql = code.getSql(conn);
            return sql.executeQuery();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 根据结果集 和 数据对象的构造逻辑,将结果集的内容封装到ArrayList集合中
     * @param resultSet 没有关闭的结果集
     * @param consumer 结果集构造对象的函数
     * @return 包含了对整个结果集函数转换后的对象集合
     */
    public List<T> toList(ResultSet resultSet, ToObject<T> consumer){
        List<T> list = new ArrayList<T>();

        try{
            while (resultSet.next()){
                list.add(consumer.getObject(resultSet));
            }
        }catch (SQLException e){
            throw new RuntimeException(e);
        }

        return list;
    }

    /**
     * 根据结果集 和 数据对象的构造逻辑,将结果集的内容保存到对象中
     * @param resultSet 没有关闭的结果集
     * @param consumer 结果集构造对象的函数
     * @return 包含了对整个结果集函数转换后的对象集合
     */
    public T toObject(ResultSet resultSet, ToObject<T> consumer){
        try {
            return consumer.getObject(resultSet);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 用于关闭数据各项连接
     * @throws SQLException 可能出现的在数据库连接关闭中的问题
     */
    public void close() throws SQLException {
        if (sql != null && !sql.isClosed()) {
            sql.close();
        }
        if (!conn.isClosed()){
            conn.close();
        }
    }
}